#!/usr/bin/env python

import os
import sys
import time
import stat
import smtplib
import nntplib
import logging
import logging.handlers
import shutil
import tempfile
import StringIO
import sqlite3

from subprocess import Popen, call, PIPE

from mailtools.emailme import Messenger
from buildtools.chrootenv import Environment
from autobbotd.bbconfig import *


class ResultSender():
    def __init__(self, logger=None):
        if logger:
            self.logger = logger

    def sendNews(
        self, 
        bodyfile, 
        nntpfrom, 
        subject, 
        newsgroup, 
        host = "svn.repo.net", 
        port = 119, 
        user = "username", 
        password = "password"
        ):
        """
        sendNews
            bodyfile - File to send to news group as the body
            nntpfrom - Who to say this post was from
            subject - Subject of the post
            newsgroup - What news group to post to
            host - The nntp host to send the post to 
            port - Port the nntp host is listening on
            user - User to login as
            password - Password for authentication

       Send a news post!
       """

        header = "From: %s\nSubject: %s\nNewsgroups: %s\nContent-Type: text/plain; charset=utf-8\n\n" % (
            nntpfrom,
            subject,
            newsgroup
        )
                
        s = nntplib.NNTP(
            host,
            int(port),
            user,
            password
        )

        output = StringIO.StringIO()
        try:
            output.write(header)
            f = open(bodyfile)
            try:
                output.write(f.read())
                output.seek(0)
                s.post(output)
            finally:
                f.close()
        except Exception, err:
            if self.logger:
                self.logger.error("Error sending to newsgroup:")
                self.logger.error(output.getvalue())
                self.logger.exception(err)
            else:
                print "Error sending to newsgroup"
        finally:
            output.close()


class UnitTestHarness():
    def processResultData(self):
        """
        ProcessResultData 
            chrootdir - Directory of chroot

        Save the data from a testAll.py run inside of a chroot
        """
        testdata = None
        coveragedata = None
        con = sqlite3.Connection(os.path.join(self.chrootdir, '.testalldata'))
        cur = con.cursor()
        try:
            cur.execute("SELECT testsrun, testfailures, testerrors FROM test_data;")
            testdata = cur.fetchall()[0]
            cur.execute("SELECT statements, statements_executed FROM coverage_data;")
            coveragedata = cur.fetchall()[0]
        finally:
            cur.close()
            con.close()

        if not testdata or not coveragedata:
            return

        numtests = int(testdata[0])
        numfailures = int(testdata[1])
        numerrors = int(testdata[2])
        statements = int(coveragedata[0])
        statements_executed = int(coveragedata[1])

        u = UnitTestResults()
        u.numtests = numtests
        u.numfailures = numfailures
        u.testrun_id = self.tr.id
        u.numerrors = numerrors
        u.statements = statements
        u.statements_executed = statements_executed
        u.logpath = " "
        u.save()

        # Get the testsuite_id for the results we saved.
        #tr = TestRuns.objects.get(id=self.runid)
        testsuite_id = TestSuiteRuns.objects.get(
                id=self.tr.testsuiterun_id
        ).testsuite_id 

        # Now figure out the previous test results for the same test suite
        # the Django'y way.
        testsuite_set = TestSuites.objects.get(id=testsuite_id)
        #self.logger.debug(dir(testsuite_set))
        testsuite_runs = testsuite_set.testsuiteruns_set.all().order_by('id').reverse()
        self.logger.debug(testsuite_runs)
        revision = testsuite_runs[0].svnrev
        prevsuite_run = testsuite_runs[1]
        prevsuite_run_id = prevsuite_run.id
        self.logger.debug("PREV SUITE RUN_ID:%s" % prevsuite_run_id)
        
        # Got the prevsuite_run_id, now let's get the runs of pyunit style tests
        # for this testsuiterun id.

        pyunittest_runs = TestRuns.objects.filter(
            testsuiterun = prevsuite_run_id,
            test__testtype = 2
        )
        self.logger.debug("PREV UNITTEST_RUNS:%s" % pyunittest_runs)

        try:
            # Let's be dumb for the moment and just use one unittest style test per 
            # suite
            pyunittest_run =  pyunittest_runs[0]
            self.logger.debug(pyunittest_run)
        except IndexError:
            self.logger.info("Could not find previous unittest run.")

        regressions = []
        if pyunittest_run:
            prevtest_results = pyunittest_run.unittestresults_set.all()[0]

            if prevtest_results.numtests > numtests:
                regressions.append("Lowered number of test cases")
            if prevtest_results.numfailures < numfailures:
                regressions.append("Increased failing tests")
            if prevtest_results.numerrors < numerrors:
                regressions.append("Increased test errors")

        if regressions:
            print regressions
            reg_text = ", ".join(regressions)
            self.messenger.send_message(
                "mail.server.com",
                self.recipients,
                "buildntest",
                "Code Regressions on %s at r%s" % (os.path.basename(self.chrootdir), revision),
                body = """The following regressions were detected: %s.""" % reg_text
            )


TESTERS = {
    2:UnitTestHarness
}


#
# Helper Functions 
#

def runTest(testcmd, chrootdir, logfile = None, errfile = None):
    """
    runTest

    Run a test against a chrootdir.
    """
    cmd = testcmd % (
        chrootdir,
    )
    cmd = cmd.replace("\r\n", "\n")

    f = open("%s_build.log" % chrootdir, "w")
    try:
        proc = Popen(
            cmd,
            shell=True,
            stdout=f,
            stderr=f
        )
        #self.logger.info("Running build...")
        proc.wait()
    finally:
        f.close()
    ret = proc.returncode
    #self.logger.info("Done with build. RET: %s" % ret)
    if ret == 0:
        buildret = True
    else:
        buildret = False

    return buildret



def tailFile(file, maxlines=20):
    # Assuming 80 char lines
    maxbytes = 80 * maxlines
    maxdata = ""
    f = open(file)
    try:
        try:
            f.seek(-maxbytes, 2)
        except IOError:
            f.seek(0)
        maxdata = f.read()
    finally:
        f.close()

    return maxdata

def getRevAuthor(dir, revision='HEAD'):
    """
    getRevAuthor
        dir - Directory of code checkout
        revision - (optional) Revision of codebase

    Attempt to get the author who made a particular revision to the code base.
    """
    author = Popen(
        """svn info -r %s %s | grep "^Last Changed Author" """ % (revision, dir),
        shell=True,
        stdout=PIPE
    ).communicate()[0].split(':')[1]

    return author
    
def getSvnPathInfo(svnpath):
    """
    getSvnRev
        svnpath - Svn path to check info of last revision
    
    OUTPUT:  author, revision, date
    """
    info = Popen(
        "svn info %s | awk ' /Last Changed/ { print $4 } '" % svnpath,
        shell = True,
        stdout = PIPE
    ).communicate()[0]
    svndata = info.split()
    if len(svndata) < 3:
        svndata = ("","","")
    return svndata

